#include <stdlib.h>
#include <stdio.h>
#include "HigherOrderFunctions.h"

void Map(struct LinkedList* list, void*(*f)(void* element))
{
  if (!IsEmpty(list))
  {
    struct Node* current_node = list ->head;
    while (current_node != 0)
    {
      void* old_element = current_node ->element;
      current_node ->element = f(current_node ->element);
      free(old_element);
      current_node = current_node ->next;
    }
  }
}

int Exists(const struct LinkedList* const list, int(*predicate)(void* element))
{
  if (IsEmpty(list))
    return 0;
  else
  {
    struct Node* current_node = list ->head;
    while (current_node != 0)
    {
      if (predicate(current_node ->element))
        return 1;
      current_node = current_node ->next;
    }
    return 0;
  }
}

struct LinkedList* Zip(const struct LinkedList* const l1, const struct LinkedList* l2)
{
  struct LinkedList* zipped_list = (struct LinkedList*)malloc(sizeof(struct LinkedList));
  struct Node *current_node1, *current_node2, *current_new_node;
  struct Pair* new_head_pair = (struct Pair*)malloc(sizeof(struct Pair));
  struct Node* new_head = (struct Node*)malloc(sizeof(struct Node));
  if (IsEmpty(l1) || IsEmpty(l2))
  {
    zipped_list ->head = 0;
    return zipped_list;
  }
  current_node1 = l1 ->head;
  current_node2 = l2 ->head;
  new_head_pair ->fst = current_node1 ->element;
  new_head_pair ->snd = current_node2 ->element;
  new_head ->element = new_head_pair;
  new_head ->next = 0;
  zipped_list ->head = new_head;
  current_node1 = l1 ->head ->next;
  current_node2 = l2 ->head ->next;
  current_new_node = new_head;
  while (current_node1 != 0 && current_node2 != 0)
  {
    struct Pair* new_pair = (struct Pair*)malloc(sizeof(struct Pair));
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    new_pair ->fst = current_node1 ->element;
    new_pair ->snd = current_node2 ->element;
    new_node ->element = new_pair;
    new_node ->next = 0;
    current_new_node ->next = new_node;
    current_node1 = current_node1 ->next;
    current_node2 = current_node2 ->next;
    current_new_node = new_node;
  }
  return zipped_list;
}